package com.winning.ds.common.data_source.factory;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.fastjson.JSON;
import com.winning.ds.common.data_source.model.DataSourceEntity;
import com.winning.ds.common.exception.BaseException;
import com.winning.ds.common.service.SysEsbSjyService;
import com.winning.ds.common.util.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @作者： caopengfei
 * @时间： 2020/9/7
 */
@Slf4j
public class JdbcTemplateFactory {

    /**
    @comment：  用于缓存已经创建过的数据库连接，防止多次重复创建数据库连接，造成数据库连接的浪费
     */
    private static Map<String, NamedParameterJdbcTemplate> jdbcTemplateMap = new HashMap<>();

    private static SysEsbSjyService sysEsbSjyService;

    /**
    * @comment：  动态获取数据库连接
    * @author： caopengfei
    * @time：  2020/9/7 15:01
    */
    public static  NamedParameterJdbcTemplate getJdbcTemplate(String dataSourceKey){
        init();
        //查看数据库连接是否已经被创建
        if(jdbcTemplateMap.containsKey(dataSourceKey) && jdbcTemplateMap.get(dataSourceKey) != null){
            return jdbcTemplateMap.get(dataSourceKey);
        }else{
            //创建jdbc连接
            try {
                NamedParameterJdbcTemplate namedParameterJdbcTemplate = createJdbcTemplate(dataSourceKey);
                jdbcTemplateMap.put(dataSourceKey, namedParameterJdbcTemplate);
                return namedParameterJdbcTemplate;
            }catch (Exception e){
                log.error("创建数据库连接:"+dataSourceKey+"失败",e);
                throw new BaseException("创建数据库连接:"+dataSourceKey+"失败，请检查项目的数据源配置！",e);
            }
        }
    }

    /**
     * 数据源修改后，删除缓存的数据源
     */
    public static void removeDatasource(String dataSourceKey){
        if(StringUtils.isEmpty(dataSourceKey)){
            return;
        }
        try{
            jdbcTemplateMap.remove(dataSourceKey);
        }catch(Exception ex){
            log.error("删除数据库连接:"+dataSourceKey+"失败",ex);
        }
    }

    /**
    * @comment： 进行基础组件的初始化
    * @author： caopengfei
    * @time：  2020/9/8 14:28
    */
    private static void init(){
        if(sysEsbSjyService == null ){
            sysEsbSjyService = SpringUtil.getBean(SysEsbSjyService.class);
        }
    }

    /**
    * @comment： 创建JdbcTemplate对象
    * @author： caopengfei
    * @time：  2020/9/7 15:03
    */
    private static NamedParameterJdbcTemplate createJdbcTemplate(String dataSourceKey) throws Exception{
        DataSourceEntity dataSourceEntity = new DataSourceEntity(sysEsbSjyService.queryDataSource(dataSourceKey));
        log.debug(dataSourceKey+"--获取到数据源配置信息："+JSON.toJSONString(dataSourceEntity));
        return createJdbcTemplate(dataSourceEntity);
    }

    /**
     * 根据的数据源信息创建JdbcTemplate
     * @param dataSourceEntity
     * @return
     * @throws Exception
     */
    public static NamedParameterJdbcTemplate createJdbcTemplate(DataSourceEntity dataSourceEntity) throws Exception{
        DruidDataSource dataSource = getDataSource(dataSourceEntity);
        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
        log.debug("创建JdbcTemplate连接成功");
        return namedParameterJdbcTemplate;
    }

    public static DruidDataSource getDataSource(DataSourceEntity dataSourceEntity)  throws Exception{
        Map<String,String> params = dataSourceEntity.toMap();
        params.put("oracle.net.CONNECT_TIMEOUT","3000");
        DruidDataSource dataSource = (DruidDataSource)DruidDataSourceFactory.createDataSource(params);
        //设置获取连接出错时的自动重连次数
        dataSource.setConnectionErrorRetryAttempts(0);
        //true表示向数据库请求连接失败后,就算后端数据库恢复正常也不进行重连,客户端对pool的请求都拒绝掉.false表示新的请求都会尝试去数据库请求connection.默认为false
        dataSource.setBreakAfterAcquireFailure(true);
        //校验连接是否可用的超时时间
        dataSource.setValidationQueryTimeout(300);
        //设置获取连接时的重试次数，-1为不重试
        dataSource.setNotFullTimeoutRetryCount(-1);
        //设置获取连接出错时是否马上返回错误，true为马上返回
        dataSource.setFailFast(true);
        //查询超时间设置
        dataSource.setQueryTimeout(600);
        //测试连接是否正常
        testDatasource(dataSource);

        return dataSource;
    }

    private static void testDatasource(DruidDataSource dataSource) throws Exception{
        StopWatch stopWatch = new StopWatch();
        try{
            stopWatch.start();
            DruidPooledConnection connection = dataSource.getConnection();
            connection.close();
            stopWatch.stop();
            log.info("连接测试成功, 耗时："+stopWatch.getTotalTimeMillis());
        }catch (Exception e){
            stopWatch.stop();
            log.error("连接测试失败"+stopWatch.getTotalTimeMillis(),e);
            throw new BaseException("数据库配置有误，请检查",e);
        }
    }

    public static boolean test(DataSourceEntity dataSourceEntity){
        try{
            getDataSource(dataSourceEntity);
            return true;
        }catch (Exception e){
            return false;
        }
    }

}
